iT邦幫忙

2025 iThome 鐵人賽

DAY 25
0
Software Development

《電商修仙術:AI × Magento 開發心法》系列 第 25

[Day 25] 實作篇:正規化 URL,解決Magento重複快取問題

  • 分享至 

  • xImage
  •  

前言

昨天(Day 24)我們把快取污染的問題講清楚:
同樣內容的頁面,卻因為 URL 帶了不同參數或順序,生成了多份快取,浪費了 Redis 空間,也拉低了命中率。

今天就是「解方落地」的一天。
我們要做的,就是在 Magento 生成 Full Page Cache 識別符的最後一步,把 URL 正規化,確保「一樣的內容 → 一樣的 Key」。


我們的做法:攔截 Identifier

Magento 的 Full Page Cache 識別符是由 Magento\Framework\App\PageCache\Identifier::getValue() 產生的。
我們透過 Plugin 包住這個方法,先攔截請求、處理參數,最後再交回快取系統。

這樣做的好處是:

  • 不改核心程式碼,乾淨安全。
  • 集中切入點,全站統一規則。
  • 可開關、可擴充,方便灰度測試與回退。

我們的策略:白名單 + 排序 + 正規化

核心思路延續 Day 24:

  1. 白名單過濾

    • 保留「會影響 HTML 輸出」的參數。
    • 白名單來源:產品屬性、預設核心參數(如分頁、排序)、後台自訂。
  2. 字母序排序

    • 相同參數組合,無論順序如何,都會整理成一致的排列。
  3. 正規化 URL

    • 只保留必要參數後,重新組裝 URL,形成「標準樣式」。
  4. 生成穩定識別符

    • 把正規化 URL、是否為 https、vary string 打包,再用 sha1 產生最終快取 key。

可控的設計:後台開關與自訂參數

為了讓這個功能能「隨時開關」、「隨時調整」,我們在後台新增了設定:

  • Enable/Disable:可以立即回退到 Magento 原生識別符。
  • Allowed Params:可輸入多行參數,讓特殊頁面或第三方模組也能被正確保留。

這讓整個機制更彈性,不會成為風險。


關鍵程式碼

Plugin:攔截並正規化

public function aroundGetValue(Identifier $subject, \Closure $proceed)
{
    try {
        $params = $this->request->getParams();
        if (empty($params) || !$this->isEnabled()) {
            return $proceed();
        }

        // 建立白名單
        $productAttributeCodes = $this->getAllProductAttributeCodes();
        $defaultFilterCodes = ['product_list_order','product_list_dir','q','p','cat','s','np'];
        $configCustomCodes = $this->getCustomAllowedParams();
        $allowedKeys = array_merge($productAttributeCodes, $defaultFilterCodes, $configCustomCodes);

        // 過濾 + 排序
        $filteredParams = $this->filterParamsByAllowedFields($params, $allowedKeys);
        ksort($filteredParams);

        // 正規化 URL
        $baseUrl = $this->getBaseUrl();
        $processedUrl = !empty($filteredParams)
            ? $baseUrl . '?' . http_build_query($filteredParams)
            : $baseUrl;

        // 生成識別符
        $data = [
            $this->request->isSecure(),
            $processedUrl,
            $this->request->get(\Magento\Framework\App\Response\Http::COOKIE_VARY_STRING)
                ?: $this->context->getVaryString()
        ];

        return sha1($this->serializer->serialize($data));

    } catch (\Exception $e) {
        ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')
            ->error('Error in page cache getValue: ' . $e->getMessage());
        return $proceed();
    }
}

實例驗證

原始請求

/catalogsearch/result/index/?MRL_filters_coftable=341&abc=222&MRL_filters_preorder=134&product_list_dir=asc&q=sofa&product_list_order=name&gad_source=1&gclid=xxx

處理後(正規化)

/catalogsearch/result/index/?MRL_filters_coftable=341&MRL_filters_preorder=134&product_list_dir=asc&product_list_order=name&q=sofa
  • abc=222gad_source=1gclid=xxx → 被移除
  • 真正會影響 HTML 的參數保留並排序
  • 結果:相同內容的頁面,終於命中同一個快取 key

我們的交付成果

  • Plugin 實作:攔截 Identifier,實作快取正規化。
  • 後台設定:可開關、可擴充的白名單機制。
  • 實測案例:前後對照 URL,確認 HTML 輸出一致。

預期效果

  • 減少重複快取 → Redis 更乾淨。
  • 提升命中率 → 用戶體驗更穩定。
  • 降低資源消耗 → I/O 與記憶體壓力下降。
  • 不影響正確性 → 頁面內容保持一致。

結語

Day 25,我們完成了最關鍵的一步:

讓 Magento 的快取 key,不再被無用參數污染。

這是一個 Plugin + 白名單 + 正規化 的實戰案例,不只解決了效能問題,也保留了彈性。

明天(Day 26),我們會進入數據觀察與成效驗證,看看這套機制帶來的真實改變。


上一篇
[Day 24] 讓相同頁面只快取一次:Identifier 正規化計畫
系列文
《電商修仙術:AI × Magento 開發心法》25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言